﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Threading.Tasks;
using DataModel;
using Microsoft.Office.Interop.Excel;
using System.Drawing;

namespace DataModel
{
    /// <summary>
    /// The static class responsible for managing the working memory related to the project specific data such as 
    /// the term sets, site columns, and content types being created by this tool.
    /// </summary>
    public static class SaveFile
    {
        public static String Title { get; set; }
        public static String Path { get; set; }
        public static String CultureCode { get; set; }

        public static TermSetList termSets = new TermSetList();
        public static SiteColumnList siteCols = new SiteColumnList();
        public static ContentTypeList contentTypes = new ContentTypeList();
        public static List<Group> scGroupsList = new List<Group>();
        public static List<String> ctGroupsList = new List<String>();
        public static Group defaultSCGroup;

        //also stuff for content types / site columns / etc

        public static string SPDump_SiteUrl;
        public static XmlElement SPDumpElement;
        public static XmlElement Default_SPDumpElement;

        public static TermSetList SPDump_termSets = new TermSetList();
        public static SiteColumnList SPDump_siteCols = new SiteColumnList();
        public static ContentTypeList SPDump_contentTypes = new ContentTypeList();


        /// <summary>
        /// Gets or sets the window title.
        /// </summary>
        /// <value>
        /// The window title.
        /// </value>
        public static String WindowTitle
        {
            get
            {
                if (Path == null) //no project open
                {
                    return "Helux - " + DataModel.Names.LongName; 
                }
                else 
                { 
                    return "Helux - " + DataModel.Names.ShortName + " :: " + Title + " (" + (Path ?? "*") + ")"; 
                }

            }
            set { }
        }

        /// <summary>
        /// Creates a new file.
        /// </summary>
        /// <param name="title">The project title.</param>
        /// <param name="path">The save path.</param>
        /// <param name="cultureCode">The culture code.</param>
        public static void NewFile(String title, String path = "", String cultureCode = "en-US")
        {
            Title = title;
            Path = path;
            CultureCode = cultureCode;
            termSets = new TermSetList();
            siteCols = new SiteColumnList();
            contentTypes = new ContentTypeList();
            defaultSCGroup = new Group(title);
            scGroupsList.Add(defaultSCGroup);

            SPDump_termSets = new TermSetList();
            SPDump_siteCols = new SiteColumnList();
            SPDump_contentTypes = new ContentTypeList();

            ctGroupsList = new List<string>(
            new[] { 
                "Business Intelligence",
                "Community Content Types",
                "Custom Content Types",
                "Digital Asset Content Types",
                "Display Template Content Types",
                "Document Content Types",
                "Document Set Content Types",
                "Folder Content Types",
                "Group Work Content Types",
                "List Content Types",
                "Special Content Types"
            });

            XmlDocument defaultSPDump = new XmlDocument();
            defaultSPDump.LoadXml(@"<?xml version=""1.0"" encoding=""utf-8"" ?>
                                    <SPDump Site=""default"">
                                      <SiteColumnsDump>
                                            <SiteColumnDump Name=""Title"" InternalName=""Title"" Group=""_Hidden"" Type=""Text"" Required=""TRUE"" TypeDisplayName=""Single line of text""/>
                                            <SiteColumnDump Name=""Name"" InternalName=""FileLeafRef"" Group=""_Hidden"" Type=""File"" Required=""TRUE"" TypeDisplayName=""File""/>
                                      </SiteColumnsDump>
                                      <ContentTypesDump>
                                            <ContentTypeDump ID=""0x01"" Name=""Item"">
                                                <Ref InternalName=""Title"" />
                                            </ContentTypeDump>
                                            <ContentTypeDump ID=""0x0101"" Name=""Document"">
                                                <Ref InternalName=""FileLeafRef"" />
                                                <Ref InternalName=""Title"" />
                                            </ContentTypeDump>
                                      </ContentTypesDump>
                                    </SPDump>");

            SPDumpElement = defaultSPDump.GetElementsByTagName("SPDump")[0] as XmlElement;
            Default_SPDumpElement = SPDumpElement;
            SPDump_SiteUrl = "default";
            //reassign all storage variables (aka, wipe 'em out!)
            reloadDump();
        }

        /// <summary>
        /// Reloads the SPDump_ members from the XmlElement, SPDumpElement.
        /// </summary>
        public static void reloadDump()
        {
            SPDump_siteCols = parseSiteColDump();
            SPDump_contentTypes = parseContentTypeDump(SPDump_siteCols);
        }

        /// <summary>
        /// Parses the site col dump.
        /// </summary>
        /// <returns></returns>
        public static SiteColumnList parseSiteColDump()
        {
            SiteColumnList retList = new SiteColumnList();

            foreach (XmlElement siteCol in SPDumpElement.GetElementsByTagName("SiteColumnDump"))
            {
                SiteColumn sc = new SiteColumn(siteCol.GetAttribute("Name"), siteCol.GetAttribute("InternalName"));
                sc.IsFromDump = true;
                try
                {
                    sc.Type = SiteColumnType.GetTypeBySPName(siteCol.GetAttribute("Type"));
                }
                catch
                {
                    sc.TypeDisplayName = siteCol.GetAttribute("TypeDisplayName");
                }
                retList.Add(sc);
            }

            return retList;
        }

        /// <summary>
        /// Parses the content type dump.
        /// </summary>
        /// <param name="SPDump_siteCols">The sp dump_site cols ref, used to modify the local SiteColumnList created in parseSiteColDump.</param>
        /// <returns></returns>
        public static ContentTypeList parseContentTypeDump(SiteColumnList SPDump_siteCols) {
            ContentTypeList retList = new ContentTypeList();
            foreach (XmlElement contentType in SPDumpElement.GetElementsByTagName("ContentTypeDump"))
            {
                ContentType ct = new ContentType(contentType.GetAttribute("Name"));
                foreach (XmlElement fieldRef in contentType.ChildNodes)
                {
                    string refVal = "";
                    try
                    {
                        refVal = fieldRef.GetAttribute("InternalName");
                        ct.SCList.Add(SPDump_siteCols.Single(sc => sc.InternalName == refVal));
                    }
                    catch (Exception ex)
                    {
                        Log.Append("parseContentTypeDump exception: [" + ct.Name + "->" + refVal + "]" + ex.Message).AppendLine();
                    }
                }

                retList.Add(ct);
            }
            return retList;
        }

        /// <summary>
        /// Loads from file.
        /// </summary>
        /// <param name="path">The save path.</param>
        /// <returns>A bool indicating success</returns>
        /// <exception cref="System.Exception">
        /// Error, more than one project found in the file.
        /// or
        /// Error, more than one SPDump found.
        /// or
        /// Error, more than one set of groups found.
        /// or
        /// Error, more than one set of managed metadata found.
        /// or
        /// Error, more than one set of site columns found.
        /// or
        /// Invalid GUID in site column:  + siteColumnNode.GetAttribute(Name)
        /// or
        /// Invalid Sitecolumn GUID
        /// or
        /// Invalid SiteColumn Name
        /// or
        /// Invalid SiteColumn Type
        /// or
        /// Could not find term or termset with guid:  + termRefGuid + \n + ex.Message
        /// or
        /// Error, there wasn't exactly one set of choices found.
        /// or
        /// Error, more than one set of content types found.
        /// or
        /// Could not find site column:  + siteColRefNode.GetAttribute(Name) +  by id.
        /// or
        /// Could not find site column:  + siteColRefNode.GetAttribute(Name) +  by id.
        /// </exception>
        public static bool LoadFromFile(String path) 
        {
            TermSetList termSets = new TermSetList();
            SiteColumnList siteCols = new SiteColumnList();
            ContentTypeList contentTypes = new ContentTypeList();
            List<Group> scGroupsList = new List<Group>();
            List<String> ctGroupsList = new List<string>();

            TermSetList SPDump_termSets = new TermSetList();
            SiteColumnList SPDump_siteCols = new SiteColumnList();
            ContentTypeList SPDump_contentTypes = new ContentTypeList();
            List<Group> SPDump_groupsList = new List<Group>();
            // These have the same name as the class member's since this method was originally non-transactional
            // By re-declaring these in this local scope it means termSets.Add(x) doesn't actually affect SaveFile.termSets until 
            // we later set SaveFile.termSets = termSets
            // This way we can parse the file's xml structure into memory and abort at any point without changing the current
            // project until we're sure everything went okay.

            
            XmlDocument saveFile = new XmlDocument();
            saveFile.Load(path);
            Path = path;
            int projectCount = 0;
            foreach (XmlElement projectNode in saveFile.GetElementsByTagName("Project"))
            {
                projectCount++;
                if (projectCount > 1)
                {
                    throw new Exception("Error, more than one project found in the file.");
                }
                NewFile(projectNode.GetAttribute("Name"), path);

                int spdumpCount = 0;
                foreach (XmlElement x in saveFile.GetElementsByTagName("SPDump"))
                {
                    spdumpCount++;
                    if (spdumpCount > 1)
                    {
                        throw new Exception("Error, more than one SPDump found.");
                    }
                    SPDumpElement = x;

                    SPDump_siteCols = parseSiteColDump();
                    SPDump_contentTypes = parseContentTypeDump(SPDump_siteCols);

                    SPDump_SiteUrl = x.GetAttribute("Site");
                }

                //Parse the Managed Metadata XML tree into memory...
                int managedMetaDataCount = 0;
                foreach (XmlElement x in saveFile.GetElementsByTagName("ManagedMetaData"))
                {
                    int groupsCount = 0;
                    foreach (XmlElement groupsNode in saveFile.GetElementsByTagName("Groups"))
                    {
                        groupsCount++;
                        if (groupsCount > 1)
                        {
                            throw new Exception("Error, more than one set of groups found.");
                        }
                        foreach (XmlElement groupNode in groupsNode.GetElementsByTagName("GroupDetails"))
                        {
                            Group g = new Group(groupNode.GetAttribute("Name"), groupNode.GetAttribute("Description"), groupNode.GetAttribute("GUID"));
                            g.Contributors = groupNode.GetAttribute("Contributors");
                            g.Managers = groupNode.GetAttribute("Managers");
                            scGroupsList.Add(g);
                        }
                    } 
                    
                    managedMetaDataCount++;
                    if (managedMetaDataCount > 1)
                    {
                        throw new Exception("Error, more than one set of managed metadata found.");
                    }
                    foreach (XmlElement termSetNode in x.GetElementsByTagName("TermSet"))
                    {
                        TermSet ts = new TermSet(termSetNode.GetAttribute("Name"), termSetNode.GetAttribute("GUID"));
                        ts.IsOpenForTermCreation = (termSetNode.GetAttribute("OpenForTermCreation").ToLower() == "true");
                        if (scGroupsList.Count(g => g.Name == termSetNode.GetAttribute("Group")) == 0) 
                        {
                            scGroupsList.Add(new Group(termSetNode.GetAttribute("Group")));
                        }
                        else
                        {
                            ts.Group = scGroupsList.Single(g => g.Name == termSetNode.GetAttribute("Group"));
                        }
                        foreach (XmlElement termNode in termSetNode)
                        {
                            if (termNode.Name == "Term")
                            {
                                Term t = ts.AddRootTerm(termNode.GetAttribute("Name"), termNode.GetAttribute("GUID"));
                                t.IsTaggable = (termNode.GetAttribute("Taggable").ToLower() == "true");
                                foreach (XmlElement subTerms in termNode)
                                {
                                    if (subTerms.Name == "Term")
                                    {
                                        BuildTermFromTermNode(subTerms, t);
                                    }
                                    else if (subTerms.Name == "Label")
                                    {
                                        t.Labels.Add(new TermLabel(subTerms.InnerText, subTerms.GetAttribute("CultureCode"), subTerms.GetAttribute("IsDefaultForLanguage").ToLower() == "true"));
                                    }
                                    else if (subTerms.Name == "SharedCustomProperty")
                                    {
                                        t.SharedCustomProperties.Add(subTerms.GetAttribute("Name"), subTerms.InnerText);
                                    }
                                    else if (subTerms.Name == "LocalCustomProperty")
                                    {
                                        t.LocalCustomProperties.Add(subTerms.GetAttribute("Name"), subTerms.InnerText);
                                    }
                                }
                            }
                            else if (termNode.Name == "SharedCustomProperty")
                            {
                                ts.SharedCustomProperties.Add(termNode.GetAttribute("Name"), termNode.InnerText);
                            }
                        }
                        termSets.Add(ts);
                    }
                }
                if (managedMetaDataCount != 1)
                {
                    Log.Append("LoadFromFile Error: managedMetaDataCount != 1").AppendLine();
                    return false;
                }

                //Parse the Site Column XML tree into memory...
                int siteColumnCount = 0;
                foreach (XmlElement siteColumnsNode in saveFile.GetElementsByTagName("SiteColumns"))
                {
                    siteColumnCount++;
                    if (siteColumnCount > 1)
                    {
                        throw new Exception("Error, more than one set of site columns found.");
                    }
                    foreach (XmlElement siteColumnNode in siteColumnsNode.GetElementsByTagName("SiteColumn"))
                    {
                        try
                        {
                            bool parseChoices = false;
                            //build the SiteColumn
                            String id;
                            try
                            {
                                id = new Guid(siteColumnNode.GetAttribute("GUID")).ToString("B");
                            }
                            catch
                            {
                                if (siteColumnNode.GetAttribute("GUID").Length > 0)
                                {
                                    throw new Exception("Invalid GUID in site column: " + siteColumnNode.GetAttribute("Name"));
                                }
                                id = GUID.get();
                            }
                            SiteColumn sc = new SiteColumn(id,
                                                            siteColumnNode.GetAttribute("Name"),
                                                            siteColumnNode.GetAttribute("InternalName"),
                                                            SiteColumnType.GetTypeByShortName(siteColumnNode.GetAttribute("Type")),
                                                            siteColumnNode.GetAttribute("Required").ToLower() == "true",
                                                            siteColumnNode.GetAttribute("Unique").ToLower() == "true",
                                                            siteColumnNode.GetAttribute("DefaultVal"),
                                                            siteColumnNode.GetAttribute("SampleVal"),
                                                            siteColumnNode.GetAttribute("Description"),
                                                            siteColumnNode.GetAttribute("Group"));
                            sc.IsFromDump = siteColumnNode.GetAttribute("IsFromDump").ToLower() == "true";
                            if (sc.ID.Length != 38)
                            {
                                throw new Exception("Invalid Sitecolumn GUID");
                            }
                            if (sc.Name.Length < 1)
                            {
                                throw new Exception("Invalid SiteColumn Name");
                            }
                            if (sc.Type == null)
                            {
                                throw new Exception("Invalid SiteColumn Type");
                            }

                            sc.additionalAttributes.Add(sc.Type, new Dictionary<string, string>());

                            if (sc.Type == SiteColumnType.SINGLE_LINE_OF_TEXT) 
                            {
                                if (!siteColumnNode.HasAttribute("MaxLength") || siteColumnNode.GetAttribute("MaxLength") == "")
                                {
                                    sc.additionalAttributes[SiteColumnType.SINGLE_LINE_OF_TEXT].Add("MaxLength", "0");
                                }
                                else
                                {
                                    sc.additionalAttributes[SiteColumnType.SINGLE_LINE_OF_TEXT].Add("MaxLength", Int32.Parse(siteColumnNode.GetAttribute("MaxLength")).ToString());
                                }
                            }
                            else if (sc.Type == SiteColumnType.MULTIPLES_LINES_OF_TEXT) 
                            {
                                if (!siteColumnNode.HasAttribute("NumLines") || siteColumnNode.GetAttribute("NumLines") == "")
                                {
                                    sc.additionalAttributes[SiteColumnType.MULTIPLES_LINES_OF_TEXT].Add("NumLines", "0");
                                }
                                else
                                {
                                    sc.additionalAttributes[SiteColumnType.MULTIPLES_LINES_OF_TEXT].Add("NumLines", Int32.Parse(siteColumnNode.GetAttribute("NumLines")).ToString());
                                }
                                sc.additionalAttributes[SiteColumnType.MULTIPLES_LINES_OF_TEXT].Add("RichText", siteColumnNode.GetAttribute("RichText"));
                                sc.additionalAttributes[SiteColumnType.MULTIPLES_LINES_OF_TEXT].Add("RichTextMode", siteColumnNode.GetAttribute("RichTextMode"));
                            }
                            else if (sc.Type == SiteColumnType.CHOICE)
                            {
                                sc.additionalAttributes[SiteColumnType.CHOICE].Add("FillIn", siteColumnNode.GetAttribute("FillIn").ToLower() == "true" ? "True" : "False");
                                sc.additionalAttributes[SiteColumnType.CHOICE].Add("Format", siteColumnNode.GetAttribute("Format"));
                                parseChoices = true;
                            }
                            else if (sc.Type == SiteColumnType.MULTICHOICE) 
                            {
                                sc.additionalAttributes[SiteColumnType.MULTICHOICE].Add("FillIn", siteColumnNode.GetAttribute("FillIn").ToLower() == "true" ? "True" : "False");
                                parseChoices = true;
                            }
                            else if (sc.Type == SiteColumnType.NUMBER) 
                            {
                                sc.additionalAttributes[SiteColumnType.NUMBER].Add("ShowAsPercentage", siteColumnNode.GetAttribute("ShowAsPercentage").ToLower() == "true" ? "True" : "False");
                                if (!siteColumnNode.HasAttribute("Min") || siteColumnNode.GetAttribute("Min") == "")
                                {
                                    sc.additionalAttributes[SiteColumnType.NUMBER].Add("Min", "0.0");
                                }
                                else
                                {
                                    sc.additionalAttributes[SiteColumnType.NUMBER].Add("Min", Double.Parse(siteColumnNode.GetAttribute("Min")).ToString());
                                }
                                if (!siteColumnNode.HasAttribute("Max") || siteColumnNode.GetAttribute("Max") == "")
                                {
                                    sc.additionalAttributes[SiteColumnType.NUMBER].Add("Max", "0.0");
                                }
                                else
                                {
                                    sc.additionalAttributes[SiteColumnType.NUMBER].Add("Max", Double.Parse(siteColumnNode.GetAttribute("Max")).ToString());
                                }
                            }
                            else if (sc.Type == SiteColumnType.CURRENCY) 
                            {
                                sc.additionalAttributes[SiteColumnType.CURRENCY].Add("CurrencyLCID", siteColumnNode.GetAttribute("CurrencyLCID"));
                                if (!siteColumnNode.HasAttribute("Min") || siteColumnNode.GetAttribute("Min") == "")
                                {
                                    sc.additionalAttributes[SiteColumnType.CURRENCY].Add("Min", "0.0");
                                }
                                else
                                {
                                    sc.additionalAttributes[SiteColumnType.CURRENCY].Add("Min", Double.Parse(siteColumnNode.GetAttribute("Min")).ToString());
                                }
                                if (!siteColumnNode.HasAttribute("Max") || siteColumnNode.GetAttribute("Max") == "")
                                {
                                    sc.additionalAttributes[SiteColumnType.CURRENCY].Add("Max", "0.0");
                                }
                                else
                                {
                                    sc.additionalAttributes[SiteColumnType.CURRENCY].Add("Max", Double.Parse(siteColumnNode.GetAttribute("Max")).ToString());
                                }
                            }
                            else if (sc.Type == SiteColumnType.DATE_AND_TIME) 
                            {
                                sc.additionalAttributes[SiteColumnType.DATE_AND_TIME].Add("FriendlyDisplay", siteColumnNode.GetAttribute("FriendlyDisplay"));
                                sc.additionalAttributes[SiteColumnType.DATE_AND_TIME].Add("DisplayFormat", siteColumnNode.GetAttribute("DisplayFormat"));
                            }
                            else if (sc.Type == SiteColumnType.LOOKUP) 
                            {

                            }
                            else if (sc.Type == SiteColumnType.YES_NO) 
                            {

                            }
                            else if (sc.Type == SiteColumnType.PERSON_OR_GROUP) 
                            {
                                sc.additionalAttributes[SiteColumnType.PERSON_OR_GROUP].Add("AllowMultipleValues", siteColumnNode.GetAttribute("AllowMultipleValues").ToLower() == "true" ? "True" : "False");
                                sc.additionalAttributes[SiteColumnType.PERSON_OR_GROUP].Add("SelectionMode", siteColumnNode.GetAttribute("SelectionMode"));
                            }
                            else if (sc.Type == SiteColumnType.HYPERLINK_OR_PICTURE) 
                            {
                                sc.additionalAttributes[SiteColumnType.HYPERLINK_OR_PICTURE].Add("FormatType", siteColumnNode.GetAttribute("FormatType"));
                            }
                            else if (sc.Type == SiteColumnType.CALCULATED) 
                            {
                                sc.additionalAttributes[SiteColumnType.CALCULATED].Add("Formula", siteColumnNode.GetAttribute("Formula"));
                            }
                            else if (sc.Type == SiteColumnType.TASK_OUTCOME) 
                            {
                                parseChoices = true;
                            }
                            else if (sc.Type == SiteColumnType.FULL_HTML_WITH_FORMATTING) 
                            {

                            }
                            else if (sc.Type == SiteColumnType.IMAGE_WITH_FORMATTING) 
                            {

                            }
                            else if (sc.Type == SiteColumnType.HYPERLINK_WITH_FORMATTING) 
                            {

                            }
                            else if (sc.Type == SiteColumnType.SUMMARY_LINKS_DATA) 
                            {

                            }
                            else if (sc.Type == SiteColumnType.RICH_MEDIA_DATA) 
                            {

                            }
                            else if (sc.Type == SiteColumnType.MANAGED_METADATA) 
                            {
                                sc.additionalAttributes[SiteColumnType.MANAGED_METADATA].Add("AllowMultipleValues", siteColumnNode.GetAttribute("AllowMultipleValues").ToLower() == "true" ? "True" : "False");
                                
                                String termRefGuid = siteColumnNode.GetAttribute("TermRef");
                                TermSet t = termSets.GetTermByGUID(termRefGuid);

                                if (t != null)
                                {
                                    //sc.TList.Add(t);
                                    sc.Tag = (t);
                                }
                                else
                                {
                                    try
                                    {
                                        t = termSets.Single(ts => ts.ID == termRefGuid);
                                        sc.Tag = t;
                                    }
                                    catch (Exception ex)
                                    {
                                        throw new Exception("Could not find term or termset with guid: " + termRefGuid + "\n" + ex.Message);
                                    }
                                }
                            }

                            if (parseChoices)
                            {
                                //parse choices
                                int choicesCount = 0;
                                foreach (XmlElement choicesNode in siteColumnNode.GetElementsByTagName("Choices"))
                                {
                                    choicesCount++;
                                    
                                    foreach (XmlElement choiceNode in choicesNode.GetElementsByTagName("Choice"))
                                    {
                                        sc.SList.Add(choiceNode.InnerText);
                                    }
                                }
                                if (choicesCount != 1)
                                {
                                    throw new Exception("Error, there wasn't exactly one set of choices found.");
                                }
                            }
                            
                            siteCols.Add(sc);
                        }
                        catch (Exception ex)
                        {
                            Log.Append("LoadFromFile Exception in SiteColumn block: ").Append(ex.Message).AppendLine();
                            return false;
                        }
                    }
                }
                if (siteColumnCount != 1)
                {
                    Log.Append("LoadFromFile Error: siteColumnCount != 1").AppendLine();
                    return false;
                }

                ctGroupsList = new List<string>(new[] { 
                    "Business Intelligence",
                    "Community Content Types",
                    "Custom Content Types",
                    "Digital Asset Content Types",
                    "Display Template Content Types",
                    "Document Content Types",
                    "Document Set Content Types",
                    "Folder Content Types",
                    "Group Work Content Types",
                    "List Content Types",
                    "Special Content Types"
                });
                Dictionary<ContentType, String> parentNamesMap = new Dictionary<ContentType, string>();
                int contentTypeCount = 0;
                foreach (XmlElement contentTypesNode in saveFile.GetElementsByTagName("ContentTypes"))
                {
                    contentTypeCount++;
                    if (contentTypeCount > 1)
                    {
                        throw new Exception("Error, more than one set of content types found.");
                    }
                    foreach (XmlElement contentTypeNode in contentTypesNode.GetElementsByTagName("ContentType"))
                    {
                        try
                        {
                            //build the ContentType
                            ContentType ct = new ContentType(contentTypeNode.GetAttribute("GUID"),
                                                            contentTypeNode.GetAttribute("Name"),
                                                            contentTypeNode.GetAttribute("Desc"),
                                                            new List<SiteColumn>(), //sclist
                                                            ContentType.ROOT_TYPE, //default parent
                                                            (contentTypeNode.GetAttribute("Group") != "" ? contentTypeNode.GetAttribute("Group") : "Custom Content Types"),
                                                            contentTypeNode.GetAttribute("Approved") == "true");
                            ct.ParentIsFromDump = contentTypeNode.GetAttribute("ParentSource") == "SPDump";
                            parentNamesMap.Add(ct, contentTypeNode.GetAttribute("Parent"));
                            if (!ctGroupsList.Contains(contentTypeNode.GetAttribute("Group")))
                            {
                                ctGroupsList.Add(contentTypeNode.GetAttribute("Group"));
                            }
                            ct.Notes = contentTypeNode.GetAttribute("ProjectNotes");
                            foreach (XmlElement siteColRefNode in contentTypeNode.GetElementsByTagName("SiteColumnRef"))
                            {
                                if (siteColRefNode.GetAttribute("IsFromDump").ToLower() == "true")
                                {
                                    SiteColumn sc = SPDump_siteCols.GetSiteColByGUID(siteColRefNode.GetAttribute("GUID"));
                                    if (sc != null)
                                    {
                                        ct.SCList.Add(sc);
                                    }
                                    else
                                    {
                                        sc = SPDump_siteCols.GetSiteColByName(siteColRefNode.GetAttribute("InternalName"));
                                        if (sc != null)
                                        {
                                            ct.SCList.Add(sc);
                                        }
                                        else
                                        {
                                            throw new Exception("Could not find site column: " + siteColRefNode.GetAttribute("Name") + " by id.");
                                        }
                                    }
                                }
                                else
                                {
                                    SiteColumn sc = siteCols.GetSiteColByGUID(siteColRefNode.GetAttribute("GUID"));
                                    if (sc != null)
                                    {
                                        ct.SCList.Add(sc);
                                    }
                                    else
                                    {
                                        sc = siteCols.GetSiteColByName(siteColRefNode.GetAttribute("InternalName"));
                                        if (sc != null)
                                        {
                                            ct.SCList.Add(sc);
                                        }
                                        else
                                        {
                                            throw new Exception("Could not find site column: " + siteColRefNode.GetAttribute("Name") + " by id.");
                                        }
                                    }
                                }
                            }
                            contentTypes.Add(ct);
                        }
                        catch (Exception ex)
                        {
                            Log.Append("LoadFromFile Exception in ContentType block: ").Append(ex.Message).AppendLine();
                            return false;
                        }
                    }

                    //Now that all content types are created, create the inheritance structure:
                    foreach (ContentType ct in contentTypes)
                    {
                        
                        try
                        {
                            if (ct.ParentIsFromDump)
                            {
                                ct.Parent = SPDump_contentTypes.Single(c => c.Name == parentNamesMap[ct]);
                            }
                            else
                            {
                                ct.Parent = contentTypes.Single(c => c.Name == parentNamesMap[ct]);
                            }
                        }
                        catch
                        {
                            ct.Parent = ContentType.ROOT_TYPE;
                        }
                    }
                }
                if (contentTypeCount != 1)
                {
                    Log.Append("LoadFromFile Error: contentTypeCount != 1").AppendLine(); 
                    return false;
                }
                //done parsing each of the three major nodes now
            }
            if (projectCount != 1)
            {
                Log.Append("LoadFromFile Error: projectCount != 1").AppendLine();
                return false;
            }

            //Since we made it this far, it's safe to assume the load went successfully.
            //Now commit the temporary storage to the static references.
            SaveFile.scGroupsList = scGroupsList;
            SaveFile.termSets = termSets;
            SaveFile.siteCols = siteCols;
            SaveFile.contentTypes = contentTypes;
            SaveFile.ctGroupsList = ctGroupsList;

            SaveFile.SPDump_termSets = SPDump_termSets;
            SaveFile.SPDump_siteCols = SPDump_siteCols;
            SaveFile.SPDump_contentTypes = SPDump_contentTypes;

            return true;
        }

        /// <summary>
        /// Saves to file.
        /// </summary>
        /// <param name="path">The path.</param>
        public static void SaveToFile(String path)
        {
            XmlDocument saveFile = new XmlDocument();
            XmlNode docNode = saveFile.CreateXmlDeclaration("1.0", "ISO-8859-1", null);
            saveFile.AppendChild(docNode);

            //root project node
            XmlNode projectNode = saveFile.CreateElement("Project");
            XmlAttribute projectName = saveFile.CreateAttribute("Name");
            projectName.Value = Title;
            projectNode.Attributes.Append(projectName);

            XmlNode SPDumpEl = saveFile.CreateElement("SPDump");
            SPDumpEl.InnerXml = SPDumpElement.InnerXml;
            XmlAttribute siteAttr = saveFile.CreateAttribute("Site");
            siteAttr.Value = SPDump_SiteUrl;
            SPDumpEl.Attributes.Append(siteAttr);
            projectNode.AppendChild(SPDumpEl);

            XmlAttribute projectLanguage = saveFile.CreateAttribute("DefaultCultureCode");
            projectLanguage.Value = CultureCode;
            projectNode.Attributes.Append(projectLanguage);

            //managed metadata node
            XmlNode managedMetadataNode = saveFile.CreateElement("ManagedMetaData");

            XmlNode groupsNode = saveFile.CreateElement("Groups");
            managedMetadataNode.AppendChild(groupsNode);

            foreach (Group gr in scGroupsList)
            {
                XmlNode groupNode = saveFile.CreateElement("GroupDetails");

                XmlAttribute groupName = saveFile.CreateAttribute("Name");
                groupName.Value = gr.Name;
                groupNode.Attributes.Append(groupName);

                XmlAttribute groupId = saveFile.CreateAttribute("GUID");
                groupId.Value = gr.Id;
                groupNode.Attributes.Append(groupId);

                XmlAttribute groupDesc = saveFile.CreateAttribute("Description");
                groupDesc.Value = gr.Description;
                groupNode.Attributes.Append(groupDesc);

                XmlAttribute groupContributors = saveFile.CreateAttribute("Contributors");
                groupContributors.Value = gr.Contributors;
                groupNode.Attributes.Append(groupContributors);

                XmlAttribute groupManagers = saveFile.CreateAttribute("Managers");
                groupManagers.Value = gr.Managers;
                groupNode.Attributes.Append(groupManagers);

                groupsNode.AppendChild(groupNode);
            }

            foreach (TermSet ts in termSets)
            {
                managedMetadataNode.AppendChild(BuildTermSetNode(ts, saveFile));
            }
            projectNode.AppendChild(managedMetadataNode);

            //site columns node
            XmlNode siteColumnsNode = saveFile.CreateElement("SiteColumns");
            foreach (SiteColumn sc in siteCols)
            {
                siteColumnsNode.AppendChild(BuildSiteColumnNode(sc, saveFile));
            }
            projectNode.AppendChild(siteColumnsNode);

            // Start the fancy shmancy content type re-ordering magic:
            contentTypes.Sort(ContentType.sortInheritanceDescending());

            //content type node
            XmlNode contentTypesNode = saveFile.CreateElement("ContentTypes");
            foreach (ContentType ct in contentTypes)
            {
                contentTypesNode.AppendChild(BuildContentTypeNode(ct, saveFile));
            }
            projectNode.AppendChild(contentTypesNode);

            saveFile.AppendChild(projectNode);
            saveFile.Save(path);
        }

        

        #region EXCEL stuff
        /// <summary>
        /// Generates the excel report.
        /// </summary>
        public static void GenerateExcelReport() 
        {
            try
            {
                String[] alphabet = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
                List<String> excelColIndexs = new List<String>(alphabet);
                for (int i = 0; i < 26; i++)
                {
                    for (int j = 0; j < 26; j++)
                    {
                        excelColIndexs.Add(alphabet[i] + "" + alphabet[j]);
                    }
                }
                int row = 1;
                int col = 0;
                //now we can use excelColIndexs[i] for i=0..702
                //if we need to go further horizontally than that I will eat my socks

                System.Drawing.ColorConverter cc = new System.Drawing.ColorConverter();
                String blue1 = "#101090";
                String blue2 = "#3030c0";
                String blue3 = "#9090ff";
                String blue4 = "#a0a0ff";

                //create some variables.. what we REALLY want are some _Worksheet references so we can get_Range and actually do stuff
                Application app = new Application();
                Workbooks workbooks = app.Workbooks;
                _Workbook workbook = workbooks.Add(XlWBATemplate.xlWBATWorksheet);
                Sheets sheets = workbook.Worksheets;
                _Worksheet worksheetRef = (_Worksheet)sheets.get_Item(1);
                //if (MainWindow.DEV_MODE_ENABLED) { app.Visible = true; } 
                //normally do not display the report until it's done being generated!

                worksheetRef.Name = "Managed Metadata";

                Range range = worksheetRef.get_Range(excelColIndexs[col] + row, excelColIndexs[col + 6] + row);
                range.Merge();
                range.set_Value(Type.Missing, DataModel.Names.ShortName + " - Managed Metadata");
                range.HorizontalAlignment = XlHAlign.xlHAlignCenter;
                range.Columns.AutoFit();

                row++;

                foreach (TermSet ts in termSets)
                {
                    range = worksheetRef.get_Range(excelColIndexs[col] + row);
                    range.Merge();
                    range.set_Value(Type.Missing, "Group:");
                    range.HorizontalAlignment = XlHAlign.xlHAlignCenter;
                    range.Columns.AutoFit();

                    col++;
                    range = worksheetRef.get_Range(excelColIndexs[col] + row);
                    range.Merge();
                    range.set_Value(Type.Missing, ts.Group.Name);
                    range.HorizontalAlignment = XlHAlign.xlHAlignCenter;
                    range.Columns.AutoFit();

                    row++;
                    col = 0;
                    DumpTerms(ts, worksheetRef, ref row, ref col, excelColIndexs);
                }

                // Add a sheet for the content types
                sheets.Add();
                worksheetRef = (_Worksheet)sheets.get_Item(1);
                worksheetRef.Name = "Content Types";
                row = 1;
                col = 0;

                range = worksheetRef.get_Range(excelColIndexs[col] + row, excelColIndexs[col + 12] + row);
                range.Merge();
                range.set_Value(Type.Missing, DataModel.Names.ShortName + " - Approval Report");
                range.HorizontalAlignment = XlHAlign.xlHAlignCenter;
                range.Columns.AutoFit();

                row++;
                col = 0;
                range = worksheetRef.get_Range(excelColIndexs[col] + row);
                range.set_Value(Type.Missing, "Project:");
                range.Columns.AutoFit();

                col++;
                range = worksheetRef.get_Range(excelColIndexs[col] + row, excelColIndexs[++col] + row);
                range.Merge();
                range.set_Value(Type.Missing, Title);
                range.Columns.AutoFit();

                col++;
                range = worksheetRef.get_Range(excelColIndexs[col] + row);
                range.set_Value(Type.Missing, "Date:");
                range.Columns.AutoFit();

                col++;
                range = worksheetRef.get_Range(excelColIndexs[col] + row, excelColIndexs[++col] + row);
                range.Merge();
                range.set_Value(Type.Missing, DateTime.Now.ToString("D"));
                range.Columns.AutoFit();

                row++;
                col = 0;
                // Big "Content Types" header
                range = worksheetRef.get_Range(excelColIndexs[col] + row, excelColIndexs[col + 12] + row);
                ExcelReportCell(range, "Content Types Report", cc.ConvertFrom(blue4));
                range.HorizontalAlignment = XlHAlign.xlHAlignCenter;

                row++;
                col = 0;
                range = worksheetRef.get_Range(excelColIndexs[col] + row, excelColIndexs[col] + (row + 1));
                ExcelReportCell(range, "Approved", cc.ConvertFrom(blue1), cc.ConvertFrom("#FFFFFF"));
                ExcelCellBorder_HeaderStyle(range);

                col++;
                range = worksheetRef.get_Range(excelColIndexs[col] + row, excelColIndexs[col] + (row + 1));
                ExcelReportCell(range, "Content Type", cc.ConvertFrom(blue1), cc.ConvertFrom("#FFFFFF"));
                ExcelCellBorder_HeaderStyle(range);

                col++;
                range = worksheetRef.get_Range(excelColIndexs[col] + row, excelColIndexs[col] + (row + 1));
                ExcelReportCell(range, "Parent", cc.ConvertFrom(blue1), cc.ConvertFrom("#FFFFFF"));
                ExcelCellBorder_HeaderStyle(range);

                col++;
                range = worksheetRef.get_Range(excelColIndexs[col] + row, excelColIndexs[col] + (row + 1));
                ExcelReportCell(range, "Group", cc.ConvertFrom(blue1), cc.ConvertFrom("#FFFFFF"));
                ExcelCellBorder_HeaderStyle(range);

                col++;
                range = worksheetRef.get_Range(excelColIndexs[col] + row, excelColIndexs[(col + 7)] + row);
                col += 7;
                ExcelReportCell(range, "Metadata Fields", cc.ConvertFrom(blue3));
                range.HorizontalAlignment = XlHAlign.xlHAlignCenter;

                col++;
                range = worksheetRef.get_Range(excelColIndexs[col] + row, excelColIndexs[col] + (row + 1));
                ExcelReportCell(range, "Notes", cc.ConvertFrom(blue1), cc.ConvertFrom("#FFFFFF"));
                ExcelCellBorder_HeaderStyle(range);

                row++;
                col = 4;
                range = worksheetRef.get_Range(excelColIndexs[col] + row);
                ExcelReportCell(range, "Name", cc.ConvertFrom(blue2), cc.ConvertFrom("#FFFFFF"));
                ExcelCellBorder_HeaderStyle(range);

                col++; ;
                range = worksheetRef.get_Range(excelColIndexs[col] + row);
                ExcelReportCell(range, "Description", cc.ConvertFrom(blue2), cc.ConvertFrom("#FFFFFF"));
                ExcelCellBorder_HeaderStyle(range);

                col++; ;
                range = worksheetRef.get_Range(excelColIndexs[col] + row);
                ExcelReportCell(range, "Type", cc.ConvertFrom(blue2), cc.ConvertFrom("#FFFFFF"));
                ExcelCellBorder_HeaderStyle(range);

                col++; ;
                range = worksheetRef.get_Range(excelColIndexs[col] + row);
                ExcelReportCell(range, "Additional Info", cc.ConvertFrom(blue2), cc.ConvertFrom("#FFFFFF"));
                ExcelCellBorder_HeaderStyle(range);

                col++; ;
                range = worksheetRef.get_Range(excelColIndexs[col] + row);
                ExcelReportCell(range, "Required", cc.ConvertFrom(blue2), cc.ConvertFrom("#FFFFFF"));
                ExcelCellBorder_HeaderStyle(range);

                col++; ;
                range = worksheetRef.get_Range(excelColIndexs[col] + row);
                ExcelReportCell(range, "Unique", cc.ConvertFrom(blue2), cc.ConvertFrom("#FFFFFF"));
                ExcelCellBorder_HeaderStyle(range);

                col++; ;
                range = worksheetRef.get_Range(excelColIndexs[col] + row);
                ExcelReportCell(range, "Sample Value", cc.ConvertFrom(blue2), cc.ConvertFrom("#FFFFFF"));
                ExcelCellBorder_HeaderStyle(range);

                col++; ;
                range = worksheetRef.get_Range(excelColIndexs[col] + row);
                ExcelReportCell(range, "Default Value", cc.ConvertFrom(blue2), cc.ConvertFrom("#FFFFFF"));
                ExcelCellBorder_HeaderStyle(range);

                foreach (ContentType ct in contentTypes)
                {
                    row++; //increment row pointer
                    col = 0; //reset column pointer
                    range = worksheetRef.get_Range(excelColIndexs[col] + row);
                    ExcelReportCell(range, (ct.IsApproved) ? "Yes" : "");
                    ExcelCellBorder_RowStyle(range);

                    col++;
                    range = worksheetRef.get_Range(excelColIndexs[col] + row);
                    ExcelReportCell(range, ct.Name);
                    ExcelCellBorder_RowStyle(range);

                    col++;
                    range = worksheetRef.get_Range(excelColIndexs[col] + row);
                    ExcelReportCell(range, ct.Parent.Name);
                    ExcelCellBorder_RowStyle(range);

                    col++;
                    range = worksheetRef.get_Range(excelColIndexs[col] + row);
                    ExcelReportCell(range, ct.Group);
                    ExcelCellBorder_RowStyle(range);

                    col++;
                    range = worksheetRef.get_Range(excelColIndexs[col+8] + row);
                    ExcelReportCell(range, ct.Notes);
                    ExcelCellBorder_RowStyle(range);

                    foreach (SiteColumn sc in ct.SCList)
                    {
                        col=4;
                        range = worksheetRef.get_Range(excelColIndexs[col] + row);
                        ExcelReportCell(range, sc.Name);
                        ExcelCellBorder_RowStyle(range);

                        col++;
                        range = worksheetRef.get_Range(excelColIndexs[col] + row);
                        ExcelReportCell(range, sc.Desc);
                        ExcelCellBorder_RowStyle(range);

                        col++;
                        range = worksheetRef.get_Range(excelColIndexs[col] + row);
                        ExcelReportCell(range, sc.Type.LongName);
                        ExcelCellBorder_RowStyle(range);

                        col++;
                        StringBuilder infoSB = new StringBuilder();
                        if (sc.Type == SiteColumnType.MANAGED_METADATA)
                        {
                            infoSB.Append("--Allowed-Terms--").AppendLine();
                            try
                            {
                                infoSB.Append(((Term)sc.Tag).GetChildrenFlat());
                            }
                            catch (Exception ex)
                            {
                                Log.Append("GenerateExcelReport Exception: ").Append(ex.Message).AppendLine();
                            }
                            
                        }
                        else if (sc.Type == SiteColumnType.CHOICE)
                        {
                            infoSB.Append("--Choices--").AppendLine();
                            foreach (String s in sc.SList)
                            {
                                infoSB.Append(s).AppendLine();
                            }
                        }
                        range = worksheetRef.get_Range(excelColIndexs[col] + row);
                        ExcelReportCell(range, infoSB.ToString());
                        ExcelCellBorder_RowStyle(range);

                        col++;
                        range = worksheetRef.get_Range(excelColIndexs[col] + row);
                        ExcelReportCell(range, (sc.Required) ? "True" : "False");
                        ExcelCellBorder_RowStyle(range);

                        col++;
                        range = worksheetRef.get_Range(excelColIndexs[col] + row);
                        ExcelReportCell(range, (sc.Unique) ? "True" : "False");
                        ExcelCellBorder_RowStyle(range);

                        col++;
                        range = worksheetRef.get_Range(excelColIndexs[col] + row);
                        ExcelReportCell(range, sc.SampleVal);
                        ExcelCellBorder_RowStyle(range);

                        col++;
                        range = worksheetRef.get_Range(excelColIndexs[col] + row);
                        ExcelReportCell(range, sc.DefaultVal);
                        ExcelCellBorder_RowStyle(range);

                        row++;
                    }
                }


                //Now that the report is populated, display the Excel window
                app.Visible = true;
            }
            catch (Exception ex)
            {
                Log.Append("GenerateExcelReport Exception: ").Append(ex.Message).AppendLine();
                if (ex.Message.Contains("factory for component"))
                {
                    Log.Append(" ^--- The above exception likely occured because this machine does not have Excel installed.").AppendLine();
                }
            }
        }

        /// <summary>
        /// Fill the range with the supplied data and applies some formatting.
        /// </summary>
        /// <param name="range">The range.</param>
        /// <param name="value">The value.</param>
        /// <param name="backgroundColor">Color of the background.</param>
        /// <param name="fontColor">Color of the font.</param>
        private static void ExcelReportCell(Range range, String value, dynamic backgroundColor = null, dynamic fontColor = null)
        {
            //if (MainWindow.DEV_MODE_ENABLED) { System.Threading.Thread.Sleep(100); }
            //this, combined with an early app.Visible = true let's you watch the report get build cell by cell!
            try
            {
                range.Merge();
                range.set_Value(Type.Missing, value);
                if (backgroundColor != null) { range.Interior.Color = backgroundColor; }
                range.Font.Color = (fontColor != null) ? fontColor : System.Drawing.Color.Black;
                range.EntireColumn.AutoFit();
                range.VerticalAlignment = XlVAlign.xlVAlignCenter;
            }
            catch (Exception ex)
            {
                Log.Append("ExcelReportCell Exception: ").Append(ex.Message).AppendLine();
            }
        }

        /// <summary>
        /// Formats the range with a header style border.
        /// </summary>
        /// <param name="range">The range.</param>
        private static void ExcelCellBorder_HeaderStyle(Range range)
        {
            range.Borders.Color = System.Drawing.Color.Black; 
            range.Borders[XlBordersIndex.xlEdgeLeft].LineStyle = Microsoft.Office.Interop.Excel.XlLineStyle.xlLineStyleNone;
            range.Borders[XlBordersIndex.xlEdgeRight].LineStyle = Microsoft.Office.Interop.Excel.XlLineStyle.xlLineStyleNone;
            range.Borders[XlBordersIndex.xlEdgeTop].LineStyle = Microsoft.Office.Interop.Excel.XlLineStyle.xlLineStyleNone;
            range.Borders[XlBordersIndex.xlEdgeBottom].LineStyle = Microsoft.Office.Interop.Excel.XlLineStyle.xlContinuous;
        }

        /// <summary>
        /// Formats the range with a row style border.
        /// </summary>
        /// <param name="range">The range.</param>
        private static void ExcelCellBorder_RowStyle(Range range)
        {
            range.Borders.Color = System.Drawing.Color.Black;
            range.Borders[XlBordersIndex.xlEdgeLeft].LineStyle = Microsoft.Office.Interop.Excel.XlLineStyle.xlLineStyleNone;
            range.Borders[XlBordersIndex.xlEdgeRight].LineStyle = Microsoft.Office.Interop.Excel.XlLineStyle.xlContinuous;
            range.Borders[XlBordersIndex.xlEdgeBottom].LineStyle = Microsoft.Office.Interop.Excel.XlLineStyle.xlLineStyleNone;
        }

        /// <summary>
        /// Dumps the terms into the excel workseet.
        /// </summary>
        /// <param name="ts">The term set.</param>
        /// <param name="ws">The worksheet.</param>
        /// <param name="row">The row.</param>
        /// <param name="col">The col.</param>
        /// <param name="excelColIndexs">The excel col indexs.</param>
        private static void DumpTerms(TermSet ts, _Worksheet ws, ref int row, ref int col, List<String> excelColIndexs)
        {
            Range range = ws.get_Range(excelColIndexs[col] + row);
            range.set_Value(Type.Missing, ts.Name);
            range.HorizontalAlignment = XlHAlign.xlHAlignLeft;
            range.Columns.AutoFit();
            col++;
            foreach (TermSet ts2 in ts)
            {
                row++;
                DumpTerms(ts2, ws, ref row, ref col, excelColIndexs);
            }
            col--;
        }
        #endregion EXCEL STUFF

        /// <summary>
        /// Builds the term set node.
        /// </summary>
        /// <param name="ts">The term set.</param>
        /// <param name="saveFile">The save file xmldocument.</param>
        /// <returns>The generated node</returns>
        private static XmlNode BuildTermSetNode(TermSet ts, XmlDocument saveFile)
        {
            XmlNode termSetNode = saveFile.CreateElement("TermSet");

            XmlAttribute termSetName = saveFile.CreateAttribute("Name");
            termSetName.Value = ts.Name;
            termSetNode.Attributes.Append(termSetName);

            XmlAttribute termSetGUID = saveFile.CreateAttribute("GUID");
            termSetGUID.Value = ts.ID;
            termSetNode.Attributes.Append(termSetGUID);

            XmlAttribute termSetGroup = saveFile.CreateAttribute("Group");
            termSetGroup.Value = ts.Group.Name;
            termSetNode.Attributes.Append(termSetGroup);

            XmlAttribute termSetOpenForTermCreation = saveFile.CreateAttribute("OpenForTermCreation");
            termSetOpenForTermCreation.Value = ts.IsOpenForTermCreation ? "True" : "False";
            termSetNode.Attributes.Append(termSetOpenForTermCreation);

            foreach (String property in ts.SharedCustomProperties.Keys)
            {
                XmlElement sharedPropNode = saveFile.CreateElement("SharedCustomProperty");

                XmlAttribute sharedPropName = saveFile.CreateAttribute("Name");
                sharedPropName.Value = property;
                sharedPropNode.Attributes.Append(sharedPropName);

                sharedPropNode.InnerText = ts.SharedCustomProperties[property];

                termSetNode.AppendChild(sharedPropNode);
            }

            foreach (Term t in ts)
            {
                XmlNode termNode = BuildTermNode(t, saveFile);
                termSetNode.AppendChild(termNode);
            }

            return termSetNode;
        }

        /// <summary>
        /// Builds the term node.
        /// </summary>
        /// <param name="t">The term.</param>
        /// <param name="saveFile">The save file xmldocument.</param>
        /// <returns>The generated node</returns>
        private static XmlNode BuildTermNode(Term t, XmlDocument saveFile)
        {
            XmlNode termNode = saveFile.CreateElement("Term");

            XmlAttribute termName = saveFile.CreateAttribute("Name");
            termName.Value = t.Name;
            termNode.Attributes.Append(termName);

            XmlAttribute termGUID = saveFile.CreateAttribute("GUID");
            termGUID.Value = t.ID;
            termNode.Attributes.Append(termGUID);

            XmlAttribute termTaggable = saveFile.CreateAttribute("Taggable");
            termTaggable.Value = (t.IsTaggable) ? "true" : "false";
            termNode.Attributes.Append(termTaggable);

            foreach (TermLabel tLabel in t.Labels)
            {
                XmlElement tLabelNode = saveFile.CreateElement("Label");

                tLabelNode.InnerText = tLabel.Name;

                XmlAttribute labelLanguage = saveFile.CreateAttribute("CultureCode");
                labelLanguage.Value = tLabel.CultureCode;
                tLabelNode.Attributes.Append(labelLanguage);

                XmlAttribute labelDefault = saveFile.CreateAttribute("IsDefaultForLanguage");
                labelDefault.Value = (tLabel.IsDefaultForLanguage) ? "true" : "false";
                tLabelNode.Attributes.Append(labelDefault);

                termNode.AppendChild(tLabelNode);
                
            }

            foreach (String propertyKey in t.LocalCustomProperties.Keys)
            {
                XmlElement localCustomPropertyNode = saveFile.CreateElement("LocalCustomProperty");

                XmlAttribute propertyName = saveFile.CreateAttribute("Name");
                propertyName.Value = propertyKey;
                localCustomPropertyNode.Attributes.Append(propertyName);

                localCustomPropertyNode.InnerText = t.LocalCustomProperties[propertyKey];

                termNode.AppendChild(localCustomPropertyNode);
            }

            foreach (String propertyKey in t.SharedCustomProperties.Keys)
            {
                XmlElement sharedCustomPropertyNode = saveFile.CreateElement("SharedCustomProperty");

                XmlAttribute propertyName = saveFile.CreateAttribute("Name");
                propertyName.Value = propertyKey;
                sharedCustomPropertyNode.Attributes.Append(propertyName);

                sharedCustomPropertyNode.InnerText = t.SharedCustomProperties[propertyKey];

                termNode.AppendChild(sharedCustomPropertyNode);
            }

            foreach (Term t2 in t)
            {
                termNode.AppendChild(BuildTermNode(t2, saveFile));
            }

            return termNode;
        }

        /// <summary>
        /// Builds the site column node.
        /// </summary>
        /// <param name="sc">The sitecolumn.</param>
        /// <param name="saveFile">The save file xmldocument.</param>
        /// <returns>The generated node</returns>
        private static XmlNode BuildSiteColumnNode(SiteColumn sc, XmlDocument saveFile)
        {
            XmlNode scNode = saveFile.CreateElement("SiteColumn");

            XmlAttribute scName = saveFile.CreateAttribute("Name");
            scName.Value = sc.Name;
            scNode.Attributes.Append(scName);

            XmlAttribute scStaticName = saveFile.CreateAttribute("InternalName");
            scStaticName.Value = sc.InternalName;
            scNode.Attributes.Append(scStaticName);

            XmlAttribute scGUID = saveFile.CreateAttribute("GUID");
            scGUID.Value = sc.ID;
            scNode.Attributes.Append(scGUID);

            XmlAttribute scDesc = saveFile.CreateAttribute("Description");
            scDesc.Value = sc.Desc;
            scNode.Attributes.Append(scDesc);

            XmlAttribute scType = saveFile.CreateAttribute("Type");
            scType.Value = sc.Type.ShortName;
            scNode.Attributes.Append(scType);

            XmlAttribute scGroup = saveFile.CreateAttribute("Group");
            scGroup.Value = sc.PutInto;
            scNode.Attributes.Append(scGroup);

            XmlAttribute fromDump = saveFile.CreateAttribute("IsFromDump");
            fromDump.Value = sc.IsFromDump ? "TRUE" : "FALSE";
            scNode.Attributes.Append(fromDump);

            if (sc.Type == SiteColumnType.CHOICE || sc.Type == SiteColumnType.MULTICHOICE || sc.Type == SiteColumnType.TASK_OUTCOME)
            {
                XmlNode choicesNode = saveFile.CreateElement("Choices");
                foreach (String s in sc.SList)
                {
                    XmlNode choiceNode = saveFile.CreateElement("Choice");

                    choiceNode.InnerText = s;

                    choicesNode.AppendChild(choiceNode);
                }
                scNode.AppendChild(choicesNode);
            }
            else if (sc.Type == SiteColumnType.MANAGED_METADATA)
            {
                TermSet t = (TermSet)sc.Tag;

                XmlAttribute termGUID = saveFile.CreateAttribute("TermRef");
                termGUID.Value = t.ID;
                scNode.Attributes.Append(termGUID);
            }

            XmlAttribute scRequired = saveFile.CreateAttribute("Required");
            scRequired.Value = sc.Required.ToString();
            scNode.Attributes.Append(scRequired);

            XmlAttribute scUnique = saveFile.CreateAttribute("Unique");
            scUnique.Value = sc.Unique.ToString();
            scNode.Attributes.Append(scUnique);

            XmlAttribute scDefault = saveFile.CreateAttribute("DefaultVal");
            scDefault.Value = sc.DefaultVal;
            scNode.Attributes.Append(scDefault);

            XmlAttribute scSample = saveFile.CreateAttribute("SampleVal");
            scSample.Value = sc.SampleVal;
            scNode.Attributes.Append(scSample);

            if (sc.additionalAttributes.ContainsKey(sc.Type))
            {
                foreach (KeyValuePair<String, String> kvp in sc.additionalAttributes[sc.Type])
                {
                    XmlAttribute attr = saveFile.CreateAttribute(kvp.Key);
                    attr.Value = kvp.Value;
                    scNode.Attributes.Append(attr);
                }
            }

            return scNode;
        }

        /// <summary>
        /// Builds the content type node.
        /// </summary>
        /// <param name="ct">The content type.</param>
        /// <param name="saveFile">The save file xmldocument.</param>
        /// <returns>The generated node</returns>
        private static XmlNode BuildContentTypeNode(ContentType ct, XmlDocument saveFile)
        {
            XmlNode ctNode = saveFile.CreateElement("ContentType");

            XmlAttribute ctName = saveFile.CreateAttribute("Name");
            ctName.Value = ct.Name;
            ctNode.Attributes.Append(ctName);

            XmlAttribute ctGUID = saveFile.CreateAttribute("GUID");
            ctGUID.Value = ct.ID;
            ctNode.Attributes.Append(ctGUID);

            XmlAttribute ctNotes = saveFile.CreateAttribute("ProjectNotes");
            ctNotes.Value = ct.Notes;
            ctNode.Attributes.Append(ctNotes);

            XmlAttribute ctGroup = saveFile.CreateAttribute("Group");
            ctGroup.Value = ct.Group;
            ctNode.Attributes.Append(ctGroup);

            XmlAttribute ctParent = saveFile.CreateAttribute("Parent");
            ctParent.Value = ct.Parent.Name;
            ctNode.Attributes.Append(ctParent);

            XmlAttribute ctParentSource = saveFile.CreateAttribute("ParentSource");
            ctParentSource.Value = ct.ParentIsFromDump ? "SPDump" : "Project";
            ctNode.Attributes.Append(ctParentSource);

            XmlAttribute ctApproved = saveFile.CreateAttribute("Approved");
            ctApproved.Value = ct.IsApproved ? "true" : "false";
            ctNode.Attributes.Append(ctApproved);

            if (ct.SCList != null)
            {
                foreach (SiteColumn sc in ct.SCList)
                {
                    XmlNode scNode = saveFile.CreateElement("SiteColumnRef");

                    XmlAttribute scName = saveFile.CreateAttribute("InternalName");
                    scName.Value = sc.InternalName;
                    scNode.Attributes.Append(scName);

                    XmlAttribute scGUID = saveFile.CreateAttribute("GUID");
                    scGUID.Value = sc.ID;
                    scNode.Attributes.Append(scGUID);

                    XmlAttribute scFromDump = saveFile.CreateAttribute("IsFromDump");
                    scFromDump.Value = sc.IsFromDump ? "TRUE" : "FALSE";
                    scNode.Attributes.Append(scFromDump);

                    ctNode.AppendChild(scNode);
                }
            }

            return ctNode;
        }

        /// <summary>
        /// Builds the term from the term node.
        /// </summary>
        /// <param name="termNode">The term node.</param>
        /// <param name="parentTerm">The parent term.</param>
        /// <returns>The generated term</returns>
        private static Term BuildTermFromTermNode(XmlElement termNode, Term parentTerm)
        {
            Term t = parentTerm.Add(termNode.GetAttribute("Name"));
            t.ID = termNode.GetAttribute("GUID");
            t.IsTaggable = (termNode.GetAttribute("Taggable") == "true");
            foreach (XmlElement childTermNode in termNode)
            {
                if (childTermNode.Name == "Term")
                {
                    BuildTermFromTermNode(childTermNode, t);
                }
                else if (childTermNode.Name == "Label")
                {
                    t.Labels.Add(new TermLabel(childTermNode.InnerText, childTermNode.GetAttribute("CultureCode"), childTermNode.GetAttribute("IsDefaultForLanguage").ToLower() == "true"));
                }
                else if (childTermNode.Name == "SharedCustomProperty")
                {
                    t.SharedCustomProperties.Add(childTermNode.GetAttribute("Name"), childTermNode.InnerText);
                }
                else if (childTermNode.Name == "LocalCustomProperty")
                {
                    t.LocalCustomProperties.Add(childTermNode.GetAttribute("Name"), childTermNode.InnerText);
                }

            }
            return t;
        }
    }
}
